<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>001-变量的解构赋值</title>
</head>
<body>

</body>
<script type="text/javascript">

    // 网址 https://es6.ruanyifeng.com/#docs/destructuring

    function func() {
        return 8848;
    }

    console.log('================== 数组解构 ==================');

    /**
     * 1: 数组解构
     *
     * 说明：定义3个变量 a, b, c。分别赋值。
     * 从数组中提取值，按照对应位置，对变量赋值
     */
    let [a, b, c] = [1, 2, 3];
    console.log("a: ", a, "b: ", b, "c:", c);

    let [, , third] = ["foo", "bar", "baz"];
    console.log("third: ", third);

    let [head, ...tail] = [1, 2, 3, 4];
    console.log("head:", head, "tail:", tail);

    // 如果解构不成功变量的值就等于 undefined
    // JavaScript 中的 undefined 是一种数据类型
    let [x, y, ...z] = ['a'];
    console.log("x:", x, "y:", y, "z:", z);


    // 不完全解构, 也可以成功
    let [d, e] = [1, 2, 3];
    console.log("d:", d, "e:", e);

    // Set 对象, 也可以使用数组的解构赋值
    let [name, age, sex] = new Set(['zs', 18, '男']);
    console.log("name:", name, "age:", age, "sex:", sex);

    // 解构赋值允许指定默认值（解构失败的兜底方案）
    let [id = 1, desc = '说明'] = [2];
    console.log("id:", id, "desc:", desc);

    // ES6 内部使用严格相等运算符（===），判断一个位置是否有值。
    // 所以，只有当一个数组成员严格等于undefined，默认值才会生效。
    // 如果一个数组成员是null，默认值就不会生效，因为null不严格等于undefined。
    console.log("typeof null:", (typeof null));
    console.log("typeof undefined:", (typeof undefined));
    let [key1 = 1] = [undefined];
    let [key2 = 1] = [null];
    console.log("key1:", key1, "key2:", key2);

    // 如果默认值是一个表达式，那么这个表达式是惰性求值的，即只有在用到的时候，才会求值。
    let [x1 = func()] = [undefined];
    console.log("x1:", x1);


    console.log('================== 对象解构 ==================');

    /**
     * 2: 对象解构（对象解构和数组解构有不同）
     *
     * 对象的解构与数组有一个重要的不同。数组的元素是按次序排列的，变量的取值由它的位置决定；
     * 而对象的属性没有次序，变量必须与属性同名，才能取到正确的值。
     */
    let {field1, field2} = {field1: 'a', field2: 3306};
    console.log('field1:', field1, 'field2', field2);

    // 如果解构失败，变量的值等于undefined
    let {field3, field4} = {field3: 'kit'};
    console.log("field3:", field3, "field4", field4);

    // 对象的解构赋值，可以很方便地将现有对象的方法，赋值到某个变量。
    let {log, sin, cos} = Math;
    console.log("cos(0):", cos(0));

    // 变量名与属性名不一致的写法
    let {foo1: baz1} = {foo1: 'aaa', bar1: 'bbb'};
    // console.log("foo1:", foo1); 出错 foo1 is not defined
    console.log("baz1:", baz1);

    // 这实际上说明，对象的解构赋值是 => let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };
    let obj1 = {first: 'hello', last: 'world'};
    let {first: f, last: l} = obj1;
    console.log("f:", f, "l:", l);

    // 与数组一样，解构也可以用于嵌套结构的对象
    let obj2 = {p1: ['Hello', {y: 'World'}]};
    // 这时p是模式，不是变量，因此不会被赋值。
    // let { p, p: [x, { y }] } = obj; 这样写才会对 p 进行赋值
    let {p1: [index1, index2]} = obj2;
    console.log("index2:", index1, "index2:", index2);

    // 对象的解构也可以指定默认值。
    let {x3 = 1} = {x3: undefined};
    console.log("x3:", x3);
    let {x4 = 3, y4 = 3} = {x4: 2};
    console.log("x4:", x4, "y4:", y4);
    let {message: msg = 'wrong'} = {message: 'success'};
    console.log("message:", msg);


    console.log('================== 注意 ==================');
    // 1. 如果要将一个已经声明的变量用于解构赋值，必须非常小心。
    // 错误的写法
    // let x5;
    // {x5} = {x5: 1};
    // SyntaxError: syntax error
    let x5;
    ({x5} = {x5: 1});
    console.log("x5:", x5);

    // 2. 解构赋值允许等号左边的模式之中，不放置任何变量名。
    // 因此，可以写出非常古怪的赋值表达式。
    // 下面的表达式虽然毫无意义，但是语法是合法的，可以执行。
    ({} = [true, false]);
    ({} = 'abc');
    ({} = []);

    // 3. 由于数组本质是特殊的对象，因此可以对数组进行对象属性的解构。
    // 对数组进行对象解构。数组arr的0键对应的值是1，[arr.length - 1]就是2键，对应的值是3。
    let arr = [1, 2, 3];
    let {0: zero, 1: one, [arr.length - 1]: three} = arr;
    console.log('zero:', zero, 'one:', one, 'three:', three);

    console.log('================== 字符串的解构赋值 ==================');
    const [c1, c2, c3] = 'hello';
    console.log('c1:', c1, 'c2:', c2, 'c3:', c3);
    // 类似数组的对象都有一个length属性，因此还可以对这个属性解构赋值。
    const {length: len} = 'hello';
    console.log('len:', len);

    console.log('================== 函数参数的解构赋值 ==================');

    function add([x, y]) {
        return x + y;
    }

    console.log('add([1, 2]):', add([1, 2]));

    // 注意，下面的写法会得到不一样的结果。
    function move({x, y} = {x: 0, y: 0}) {
        return [x, y];
    }

    console.log('move({x: 3, y: 8}):', move({x: 3, y: 8}));
    console.log('move({x:3}):', move({x: 3}));
    console.log('{}:', move({}));
    console.log(move());


    console.log('================== 使用解构遍历Map ==================');
    const map = new Map();
    map.set('first', 'hello');
    map.set('second', 'world');
    for (let [k, v] of map) {
        console.log('k=', k, 'v=', v);
    }
</script>
</html>